package com.example.demo.service.task;

import com.example.demo.common.handler.CustomException;
import com.example.demo.entity.dto.TaskDTO;
import com.example.demo.service.utils.CommonUtils;
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;
import org.springframework.stereotype.Service;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * @author: ak
 * @date: 2020/5/5 10:08
 * @description:
 */
@Service
public class TimedTaskService {
    /**
     * 存储任务key和data的map
     */
    private final Map<String, TaskDTO> taskMap = new HashMap<>();
    /**
     * 任务管理类
     */
    private final Scheduler scheduler;

    public TimedTaskService() throws SchedulerException {
        this.scheduler = getScheduler();
    }

    /**
     * 发布任务
     */
    public Object publishTask(TaskDTO taskDTO) throws CustomException {
        try {
            String triggerKey = getTriggerKey(taskDTO.getTaskKey());
            String triggerGroupKey = getTriggerGroupKey(taskDTO.getTaskKey());
            //获取scheduler管理对象
            Scheduler scheduler = this.scheduler;
            //将发布的任务存储起来,等任务结束时再删除
            setTableDataMap(triggerKey, taskDTO);
            //创建JobDetail实例，并与PrintWordsJob类绑定(Job执行内容)
            JobDetail jobDetail = JobBuilder.newJob(TaskJob.class)
                    .withIdentity(triggerKey, triggerGroupKey)
                    .build();
            //指定周期性定时任务
            CronTrigger cronTrigger = getCronTrigger(taskDTO);
            //执行
            scheduler.scheduleJob(jobDetail, cronTrigger);
            System.out.println("--------定时任务发布成功 ! ------------");
            System.out.println("当前任务key：" + taskDTO.getTaskKey());
            scheduler.start();

            HashMap<Object, Object> map = new HashMap<>(16);
            taskDTO.setCron(cronTrigger.getCronExpression());
            taskDTO.setStart(CommonUtils.formatTime(cronTrigger.getStartTime().getTime()));
            taskDTO.setEnd(CommonUtils.formatTime(cronTrigger.getEndTime().getTime()));
            map.put("msg", "发布成功");
            map.put("status", 200);
            map.put("data", taskDTO);
            return map;

        } catch (SchedulerException e) {
            throw new CustomException("任务已存在", 500);
        }
    }

    /**
     * 获取发布的任务的信息
     */
    public Object getTask(String taskKey) throws SchedulerException {
        Scheduler scheduler = this.scheduler;
        String triggerKey = getTriggerKey(taskKey);
        String triggerGroupKey = getTriggerGroupKey(taskKey);
        Trigger trigger = scheduler.getTrigger(new TriggerKey(triggerKey, triggerGroupKey));
        HashMap<Object, Object> map = new HashMap<>(16);
        if (trigger == null) {
            map.put("msg", "该任务已经结束");
            map.put("status", 400);
            return map;
        }
        CronTrigger cronTrigger = (CronTrigger) trigger;
        map.put("data", trigger.getJobDataMap().get(triggerKey));
        map.put("startTime", CommonUtils.formatTime(trigger.getStartTime().getTime()));
        map.put("endTime", CommonUtils.formatTime(trigger.getEndTime().getTime()));
        map.put("taskKey", trigger.getKey());
        map.put("cron", cronTrigger.getCronExpression());
        return map;
    }

    public Scheduler getScheduler() throws SchedulerException {
        SchedulerFactory schedulerFactory = new StdSchedulerFactory();
        return schedulerFactory.getScheduler();
    }

    /**
     * 修改任务时间
     */
    public Object changeTaskTime(TaskDTO taskDTO) throws SchedulerException {
        HashMap<Object, Object> map = new HashMap<>(16);
        String triggerKey = getTriggerKey(taskDTO.getTaskKey());
        String triggerGroupKey = getTriggerGroupKey(taskDTO.getTaskKey());
        TriggerKey triggerKey1 = new TriggerKey(triggerKey, triggerGroupKey);
        if (this.scheduler.getTrigger(triggerKey1) == null) {
            map.put("msg", "任务不存在或者已经结束");
            map.put("status", 400);
            return map;
        }
        CronTrigger cronTrigger = getCronTrigger(taskDTO);
        //修改时间
        this.scheduler.rescheduleJob(triggerKey1, cronTrigger);
        taskDTO.setCron(cronTrigger.getCronExpression());
        taskDTO.setStart(CommonUtils.formatTime(cronTrigger.getStartTime().getTime()));
        taskDTO.setEnd(CommonUtils.formatTime(cronTrigger.getEndTime().getTime()));
        map.put("msg", "任务" + taskDTO.getTaskKey() + "修改成功");
        map.put("status", 200);
        map.put("data", taskDTO);
        return map;
    }

    public CronTrigger getCronTrigger(TaskDTO taskDTO) {
        Long startTime = System.currentTimeMillis();
        Long endTime = taskDTO.getEndTime();
        //如果changeTime不为空，则说明需要修改时间
        if (taskDTO.getChangeTime() != null) {
            endTime = taskDTO.getChangeTime();
        }
        if (taskDTO.getStartTime() != null) {
            startTime = taskDTO.getStartTime();
        }
        //如果cron为空，则直接指定结束时间为任务触发时间
        String cron = taskDTO.getCron();
        if (cron == null) {
            cron = CommonUtils.getCronString(endTime);
        }
        //如果数据为空，则说明不需要修改数据，直接使用上次的数据
        if (taskDTO.getData() == null) {
            TaskDTO task = getTaskMap().get(getTriggerKey(taskDTO.getTaskKey()));
            taskDTO.setData(task.getData());
        }
        String triggerKey = getTriggerKey(taskDTO.getTaskKey());
        String triggerGroupKey = getTriggerGroupKey(taskDTO.getTaskKey());
        return TriggerBuilder.newTrigger()
                .withIdentity(triggerKey, triggerGroupKey)
                //存储数据
                .usingJobData(triggerKey, taskDTO.getData())
                //立即生效
                .startNow()
                .startAt(new Date(startTime))
                .endAt(new Date(endTime))
                .withSchedule(CronScheduleBuilder.cronSchedule(cron))
                .build();
    }

    public String getTriggerKey(String taskKey) {
        return "trigger-" + taskKey;
    }

    public String getTriggerGroupKey(String taskKey) {
        return "triggerGroup-" + taskKey;
    }

    public Map<String, TaskDTO> getTaskMap() {
        return taskMap;
    }

    public void setTableDataMap(String key, TaskDTO json) {
        this.taskMap.put(key, json);
    }

}


